import logging
from typing import Dict, List, Any

import networkx as nx
from pyvis.network import Network

from 程序核心代码.ast节点.节点分析.可视化.可视化器基类 import 可视化器基类
from 程序核心代码.ast节点.节点定义.基础定义.节点异常定义 import 节点异常

logger = logging.getLogger(__name__)

class 控制流可视化器(可视化器基类):
    """控制流可视化器类，用于可视化控制流分析结果"""
    
    def __init__(self):
        super().__init__()
        self.图 = nx.DiGraph()
        self.网络 = Network(
            height="750px",
            width="100%",
            bgcolor="#ffffff",
            font_color="#333333"
        )
        self.节点样式 = {
            "开始": {"color": "#4CAF50", "shape": "box"},
            "结束": {"color": "#F44336", "shape": "box"},
            "条件": {"color": "#FF9800", "shape": "diamond"},
            "循环": {"color": "#2196F3", "shape": "ellipse"},
            "顺序": {"color": "#9C27B0", "shape": "dot"}
        }
        
    def 可视化(self, 数据: Dict[str, Any]) -> str:
        """将控制流分析结果可视化为交互式图形"""
        try:
            self.数据 = 数据
            self._构建控制流图()
            self._应用样式()
            self._添加交互()
            
            return self._生成HTML()
            
        except Exception as e:
            logger.error(f"控制流可视化失败: {str(e)}")
            raise 节点异常(f"控制流可视化失败: {str(e)}")
            
    def _构建控制流图(self) -> None:
        """构建控制流图"""
        # 添加节点
        for 节点ID, 节点信息 in self.数据["节点信息"].items():
            self.图.add_node(
                节点ID,
                label=节点信息.get("标签", 节点ID),
                title=self._生成节点提示(节点信息),
                类型=节点信息.get("类型", "未知")
            )
            
        # 添加控制流边
        for 流向 in self.数据["控制流向"]:
            源节点 = 流向["源"]
            目标节点 = 流向["目标"]
            条件 = 流向.get("条件", "")
            
            self.图.add_edge(
                源节点,
                目标节点,
                title=条件,
                arrows="to",
                label=条件
            )
            
    def _应用样式(self) -> None:
        """应用节点和边的样式"""
        self.网络.from_nx(self.图)
        
        for 节点 in self.网络.nodes:
            节点类型 = 节点.get("类型", "未知")
            样式 = self.节点样式.get(节点类型, {"color": "#999999", "shape": "dot"})
            节点.update(样式)
            
    def _添加交互(self) -> None:
        """添加交互功能"""
        self.网络.toggle_physics(True)
        self.网络.show_buttons(filter_=['physics'])
        
        # 添加事件处理
        self.网络.set_options("""
        var options = {
            "physics": {
                "hierarchicalRepulsion": {
                    "centralGravity": 0.0,
                    "springLength": 100,
                    "springConstant": 0.01,
                    "nodeDistance": 120
                },
                "maxVelocity": 50,
                "minVelocity": 0.1,
                "solver": "hierarchicalRepulsion"
            },
            "layout": {
                "hierarchical": {
                    "enabled": true,
                    "direction": "UD",
                    "sortMethod": "directed"
                }
            },
            "interaction": {
                "hover": true,
                "tooltipDelay": 300,
                "zoomView": true,
                "dragView": true
            }
        }
        """)
        
    def _生成节点提示(self, 节点信息: Dict[str, Any]) -> str:
        """生成节点的悬停提示信息"""
        提示 = [
            f"ID: {节点信息.get('id', '未知')}",
            f"类型: {节点信息.get('类型', '未知')}",
            f"标签: {节点信息.get('标签', '未知')}"
        ]
        
        if "代码" in 节点信息:
            提示.append(f"代码: {节点信息['代码']}")
            
        if "复杂度" in 节点信息:
            提示.append(f"复杂度: {节点信息['复杂度']}")
            
        return "\n".join(提示)
        
    def _生成HTML(self) -> str:
        """生成可视化HTML"""
        try:
            模板 = self.加载模板("控制流")
            图形数据 = self.网络.generate_html()
            
            return 模板.replace(
                "<!-- GRAPH_PLACEHOLDER -->",
                图形数据
            )
            
        except Exception as e:
            logger.error(f"HTML生成失败: {str(e)}")
            raise 节点异常(f"HTML生成失败: {str(e)}")
            
    def 导出图片(self, 文件名: str) -> str:
        """将控制流图导出为图片"""
        try:
            import matplotlib.pyplot as plt
            
            plt.figure(figsize=(12, 8))
            pos = nx.spring_layout(self.图)
            
            # 绘制节点
            for 类型, 样式 in self.节点样式.items():
                节点列表 = [n for n, attr in self.图.nodes(data=True) 
                        if attr.get("类型") == 类型]
                nx.draw_networkx_nodes(
                    self.图, pos,
                    nodelist=节点列表,
                    node_color=样式["color"],
                    node_shape=样式["shape"],
                    node_size=1000
                )
                
            # 绘制边
            nx.draw_networkx_edges(
                self.图, pos,
                edge_color="#666666",
                arrows=True,
                arrowsize=20
            )
            
            # 添加标签
            标签 = nx.get_node_attributes(self.图, "label")
            nx.draw_networkx_labels(self.图, pos, 标签)
            
            # 添加边标签
            边标签 = nx.get_edge_attributes(self.图, "label")
            nx.draw_networkx_edge_labels(self.图, pos, 边标签)
            
            # 保存图片
            plt.axis("off")
            图片路径 = os.path.join(self.配置["输出目录"], f"{文件名}.png")
            plt.savefig(图片路径, bbox_inches="tight", dpi=300)
            plt.close()
            
            return 图片路径
            
        except Exception as e:
            logger.error(f"图片导出失败: {str(e)}")
            raise 节点异常(f"图片导出失败: {str(e)}")
            
    def 生成控制流报告(self) -> str:
        """生成控制流分析报告"""
        try:
            报告模板 = self.加载模板("控制流报告")
            
            # 统计数据
            节点统计 = {
                "总数": len(self.数据["节点信息"]),
                "条件ast节点基础模型": len([n for n in self.数据["节点信息"].values()
                            if n.get("类型") == "条件"]),
                "循环ast节点基础模型": len([n for n in self.数据["节点信息"].values()
                            if n.get("类型") == "循环"]),
                "顺序节点": len([n for n in self.数据["节点信息"].values() 
                            if n.get("类型") == "顺序"])
            }
            
            路径统计 = self._计算路径统计()
            复杂度统计 = self._计算复杂度统计()
            
            报告数据 = {
                "节点统计": 节点统计,
                "路径统计": 路径统计,
                "复杂度统计": 复杂度统计,
                "问题节点": self._查找问题节点()
            }
            
            return 报告模板.format(**报告数据)
            
        except Exception as e:
            logger.error(f"控制流报告生成失败: {str(e)}")
            raise 节点异常(f"控制流报告生成失败: {str(e)}")
            
    def _计算路径统计(self) -> Dict[str, int]:
        """计算控制流路径统计"""
        return {
            "总路径数": len(self.数据["控制流向"]),
            "条件分支": len([f for f in self.数据["控制流向"] 
                        if "条件" in f]),
            "循环路径": len([f for f in self.数据["控制流向"] 
                        if any(n.get("类型") == "循环" 
                              for n in [self.数据["节点信息"].get(f["源"]), 
                                      self.数据["节点信息"].get(f["目标"])])]),
            "顺序路径": len([f for f in self.数据["控制流向"] 
                        if not "条件" in f and 
                        all(n.get("类型") != "循环" 
                            for n in [self.数据["节点信息"].get(f["源"]), 
                                    self.数据["节点信息"].get(f["目标"])])])
        }
        
    def _计算复杂度统计(self) -> Dict[str, Any]:
        """计算控制流复杂度统计"""
        复杂度列表 = [
            n.get("复杂度", 1) 
            for n in self.数据["节点信息"].values()
        ]
        
        return {
            "总复杂度": sum(复杂度列表),
            "平均复杂度": sum(复杂度列表) / len(复杂度列表),
            "最大复杂度": max(复杂度列表),
            "最小复杂度": min(复杂度列表)
        }
        
    def _查找问题节点(self) -> List[Dict[str, Any]]:
        """查找控制流中的问题节点"""
        问题节点 = []
        
        for 节点ID, 节点信息 in self.数据["节点信息"].items():
            # 检查复杂度过高的节点
            if 节点信息.get("复杂度", 1) > 10:
                问题节点.append({
                    "节点模型": 节点ID,
                    "类型": "复杂度过���",
                    "描述": f"节点复杂度({节点信息['复杂度']})超过建议值(10)",
                    "建议": "考虑拆分复杂的控制流逻辑"
                })
                
            # 检查深度嵌套的条件节点
            if 节点信息.get("类型") == "条件" and 节点信息.get("嵌套深度", 0) > 3:
                问题节点.append({
                    "节点模型": 节点ID,
                    "类型": "嵌套过深",
                    "描述": f"条件嵌套深度({节点信息['嵌套深度']})过高",
                    "建议": "考虑重构条件逻辑，减少嵌套层数"
                })
                
            # 检查可能的无限循环
            if 节点信息.get("类型") == "循环" and not 节点信息.get("有退出条件", True):
                问题节点.append({
                    "节点模型": 节点ID,
                    "类型": "潜在无限循环",
                    "描述": "循环缺少明确的退出条件",
                    "建议": "添加适当的循环终止条件"
                })
                
        return 问题节点
        
    def 重置(self) -> None:
        """重置可视化器状态"""
        super().清理数据()
        self.图.clear()
        self.网络 = Network(
            height="750px",
            width="100%",
            bgcolor="#ffffff",
            font_color="#333333"
        )
